// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/program_manager.h"

#include <algorithm>
#include <set>
#include <utility>
#include <vector>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/numerics/safe_math.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/program_cache.h"
#include "gpu/command_buffer/service/shader_manager.h"
//#include "third_party/re2/re2/re2.h"

using base::TimeDelta;
using base::TimeTicks;

namespace gpu {
namespace gles2 {

    namespace {

        int ShaderTypeToIndex(GLenum shader_type)
        {
            switch (shader_type) {
            case GL_VERTEX_SHADER:
                return 0;
            case GL_FRAGMENT_SHADER:
                return 1;
            default:
                NOTREACHED();
                return 0;
            }
        }

        // Given a name like "foo.bar[123].moo[456]" sets new_name to "foo.bar[123].moo"
        // and sets element_index to 456. returns false if element expression was not a
        // whole decimal number. For example: "foo[1b2]"
        bool GetUniformNameSansElement(
            const std::string& name, int* element_index, std::string* new_name)
        {
            DCHECK(element_index);
            DCHECK(new_name);
            if (name.size() < 3 || name[name.size() - 1] != ']') {
                *element_index = 0;
                *new_name = name;
                return true;
            }

            // Look for an array specification.
            size_t open_pos = name.find_last_of('[');
            if (open_pos == std::string::npos || open_pos >= name.size() - 2) {
                return false;
            }

            base::CheckedNumeric<GLint> index = 0;
            size_t last = name.size() - 1;
            for (size_t pos = open_pos + 1; pos < last; ++pos) {
                int8 digit = name[pos] - '0';
                if (digit < 0 || digit > 9) {
                    return false;
                }
                index = index * 10 + digit;
            }
            if (!index.IsValid()) {
                return false;
            }

            *element_index = index.ValueOrDie();
            *new_name = name.substr(0, open_pos);
            return true;
        }

        bool IsBuiltInFragmentVarying(const std::string& name)
        {
            // Built-in variables for fragment shaders.
            const char* kBuiltInVaryings[] = {
                "gl_FragCoord",
                "gl_FrontFacing",
                "gl_PointCoord"
            };
            for (size_t ii = 0; ii < arraysize(kBuiltInVaryings); ++ii) {
                if (name == kBuiltInVaryings[ii])
                    return true;
            }
            return false;
        }

        bool IsBuiltInInvariant(
            const VaryingMap& varyings, const std::string& name)
        {
            VaryingMap::const_iterator hit = varyings.find(name);
            if (hit == varyings.end())
                return false;
            return hit->second.isInvariant;
        }

        uint32 ComputeOffset(const void* start, const void* position)
        {
            return static_cast<const uint8*>(position) - static_cast<const uint8*>(start);
        }

    } // anonymous namespace.

    Program::UniformInfo::UniformInfo()
        : size(0)
        , type(GL_NONE)
        , fake_location_base(0)
        , is_array(false)
    {
    }

    Program::UniformInfo::UniformInfo(GLsizei _size,
        GLenum _type,
        int _fake_location_base,
        const std::string& _name)
        : size(_size)
        , type(_type)
        , accepts_api_type(0)
        , fake_location_base(_fake_location_base)
        , is_array(false)
        , name(_name)
    {
        switch (type) {
        case GL_INT:
            accepts_api_type = kUniform1i;
            break;
        case GL_INT_VEC2:
            accepts_api_type = kUniform2i;
            break;
        case GL_INT_VEC3:
            accepts_api_type = kUniform3i;
            break;
        case GL_INT_VEC4:
            accepts_api_type = kUniform4i;
            break;

        case GL_UNSIGNED_INT:
            accepts_api_type = kUniform1ui;
            break;
        case GL_UNSIGNED_INT_VEC2:
            accepts_api_type = kUniform2ui;
            break;
        case GL_UNSIGNED_INT_VEC3:
            accepts_api_type = kUniform3ui;
            break;
        case GL_UNSIGNED_INT_VEC4:
            accepts_api_type = kUniform4ui;
            break;

        case GL_BOOL:
            accepts_api_type = kUniform1i | kUniform1ui | kUniform1f;
            break;
        case GL_BOOL_VEC2:
            accepts_api_type = kUniform2i | kUniform2ui | kUniform2f;
            break;
        case GL_BOOL_VEC3:
            accepts_api_type = kUniform3i | kUniform3ui | kUniform3f;
            break;
        case GL_BOOL_VEC4:
            accepts_api_type = kUniform4i | kUniform4ui | kUniform4f;
            break;

        case GL_FLOAT:
            accepts_api_type = kUniform1f;
            break;
        case GL_FLOAT_VEC2:
            accepts_api_type = kUniform2f;
            break;
        case GL_FLOAT_VEC3:
            accepts_api_type = kUniform3f;
            break;
        case GL_FLOAT_VEC4:
            accepts_api_type = kUniform4f;
            break;

        case GL_FLOAT_MAT2:
            accepts_api_type = kUniformMatrix2f;
            break;
        case GL_FLOAT_MAT3:
            accepts_api_type = kUniformMatrix3f;
            break;
        case GL_FLOAT_MAT4:
            accepts_api_type = kUniformMatrix4f;
            break;

        case GL_FLOAT_MAT2x3:
            accepts_api_type = kUniformMatrix2x3f;
            break;
        case GL_FLOAT_MAT2x4:
            accepts_api_type = kUniformMatrix2x4f;
            break;
        case GL_FLOAT_MAT3x2:
            accepts_api_type = kUniformMatrix3x2f;
            break;
        case GL_FLOAT_MAT3x4:
            accepts_api_type = kUniformMatrix3x4f;
            break;
        case GL_FLOAT_MAT4x2:
            accepts_api_type = kUniformMatrix4x2f;
            break;
        case GL_FLOAT_MAT4x3:
            accepts_api_type = kUniformMatrix4x3f;
            break;

        case GL_SAMPLER_2D:
        case GL_SAMPLER_2D_RECT_ARB:
        case GL_SAMPLER_CUBE:
        case GL_SAMPLER_3D_OES:
        case GL_SAMPLER_EXTERNAL_OES:
        case GL_SAMPLER_2D_ARRAY:
        case GL_SAMPLER_2D_SHADOW:
        case GL_SAMPLER_2D_ARRAY_SHADOW:
        case GL_INT_SAMPLER_2D:
        case GL_INT_SAMPLER_3D:
        case GL_INT_SAMPLER_CUBE:
        case GL_INT_SAMPLER_2D_ARRAY:
        case GL_UNSIGNED_INT_SAMPLER_2D:
        case GL_UNSIGNED_INT_SAMPLER_3D:
        case GL_UNSIGNED_INT_SAMPLER_CUBE:
        case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
            accepts_api_type = kUniform1i;
            break;

        default:
            NOTREACHED() << "Unhandled UniformInfo type " << type;
            break;
        }
    }

    Program::UniformInfo::~UniformInfo() { }

    bool ProgramManager::IsInvalidPrefix(const char* name, size_t length)
    {
        static const char kInvalidPrefix[] = { 'g', 'l', '_' };
        return (length >= sizeof(kInvalidPrefix) && memcmp(name, kInvalidPrefix, sizeof(kInvalidPrefix)) == 0);
    }

    Program::Program(ProgramManager* manager, GLuint service_id)
        : manager_(manager)
        , use_count_(0)
        , max_attrib_name_length_(0)
        , max_uniform_name_length_(0)
        , service_id_(service_id)
        , deleted_(false)
        , valid_(false)
        , link_status_(false)
        , uniforms_cleared_(false)
        , num_uniforms_(0)
        , transform_feedback_buffer_mode_(GL_NONE)
    {
        manager_->StartTracking(this);
    }

    void Program::Reset()
    {
        valid_ = false;
        link_status_ = false;
        num_uniforms_ = 0;
        max_uniform_name_length_ = 0;
        max_attrib_name_length_ = 0;
        attrib_infos_.clear();
        uniform_infos_.clear();
        sampler_indices_.clear();
        attrib_location_to_index_map_.clear();
    }

    std::string Program::ProcessLogInfo(
        const std::string& log)
    {
        std::string output;
        //   re2::StringPiece input(log);
        //   std::string prior_log;
        //   std::string hashed_name;
        //   while (RE2::Consume(&input,
        //                       "(.*?)(webgl_[0123456789abcdefABCDEF]+)",
        //                       &prior_log,
        //                       &hashed_name)) {
        //     output += prior_log;
        //
        //     const std::string* original_name =
        //         GetOriginalNameFromHashedName(hashed_name);
        //     if (original_name)
        //       output += *original_name;
        //     else
        //       output += hashed_name;
        //   }
        //
        //   return output + input.as_string();
        DebugBreak();
        return output;
    }

    void Program::UpdateLogInfo()
    {
        GLint max_len = 0;
        glGetProgramiv(service_id_, GL_INFO_LOG_LENGTH, &max_len);
        if (max_len == 0) {
            set_log_info(NULL);
            return;
        }
        scoped_ptr<char[]> temp(new char[max_len]);
        GLint len = 0;
        glGetProgramInfoLog(service_id_, max_len, &len, temp.get());
        DCHECK(max_len == 0 || len < max_len);
        DCHECK(len == 0 || temp[len] == '\0');
        std::string log(temp.get(), len);
        set_log_info(ProcessLogInfo(log).c_str());
    }

    void Program::ClearUniforms(
        std::vector<uint8>* zero_buffer)
    {
        DCHECK(zero_buffer);
        if (uniforms_cleared_) {
            return;
        }
        uniforms_cleared_ = true;
        for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
            const UniformInfo& uniform_info = uniform_infos_[ii];
            if (!uniform_info.IsValid()) {
                continue;
            }
            GLint location = uniform_info.element_locations[0];
            GLsizei size = uniform_info.size;
            uint32 unit_size = GLES2Util::GetElementCountForUniformType(uniform_info.type) * GLES2Util::GetElementSizeForUniformType(uniform_info.type);
            DCHECK_LT(0u, unit_size);
            uint32 size_needed = size * unit_size;
            if (size_needed > zero_buffer->size()) {
                zero_buffer->resize(size_needed, 0u);
            }
            const void* zero = &(*zero_buffer)[0];
            switch (uniform_info.type) {
            case GL_FLOAT:
                glUniform1fv(location, size, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_VEC2:
                glUniform2fv(location, size, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_VEC3:
                glUniform3fv(location, size, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_VEC4:
                glUniform4fv(location, size, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_INT:
            case GL_BOOL:
            case GL_SAMPLER_2D:
            case GL_SAMPLER_CUBE:
            case GL_SAMPLER_EXTERNAL_OES: // extension.
            case GL_SAMPLER_2D_RECT_ARB: // extension.
                glUniform1iv(location, size, reinterpret_cast<const GLint*>(zero));
                break;
            case GL_INT_VEC2:
            case GL_BOOL_VEC2:
                glUniform2iv(location, size, reinterpret_cast<const GLint*>(zero));
                break;
            case GL_INT_VEC3:
            case GL_BOOL_VEC3:
                glUniform3iv(location, size, reinterpret_cast<const GLint*>(zero));
                break;
            case GL_INT_VEC4:
            case GL_BOOL_VEC4:
                glUniform4iv(location, size, reinterpret_cast<const GLint*>(zero));
                break;
            case GL_FLOAT_MAT2:
                glUniformMatrix2fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT3:
                glUniformMatrix3fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT4:
                glUniformMatrix4fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;

            // ES3 types.
            case GL_UNSIGNED_INT:
                glUniform1uiv(location, size, reinterpret_cast<const GLuint*>(zero));
                break;
            case GL_SAMPLER_3D:
            case GL_SAMPLER_2D_SHADOW:
            case GL_SAMPLER_2D_ARRAY:
            case GL_SAMPLER_2D_ARRAY_SHADOW:
            case GL_SAMPLER_CUBE_SHADOW:
            case GL_INT_SAMPLER_2D:
            case GL_INT_SAMPLER_3D:
            case GL_INT_SAMPLER_CUBE:
            case GL_INT_SAMPLER_2D_ARRAY:
            case GL_UNSIGNED_INT_SAMPLER_2D:
            case GL_UNSIGNED_INT_SAMPLER_3D:
            case GL_UNSIGNED_INT_SAMPLER_CUBE:
            case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
                glUniform1iv(location, size, reinterpret_cast<const GLint*>(zero));
                break;
            case GL_UNSIGNED_INT_VEC2:
                glUniform2uiv(location, size, reinterpret_cast<const GLuint*>(zero));
                break;
            case GL_UNSIGNED_INT_VEC3:
                glUniform3uiv(location, size, reinterpret_cast<const GLuint*>(zero));
                break;
            case GL_UNSIGNED_INT_VEC4:
                glUniform4uiv(location, size, reinterpret_cast<const GLuint*>(zero));
                break;
            case GL_FLOAT_MAT2x3:
                glUniformMatrix2x3fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT3x2:
                glUniformMatrix3x2fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT2x4:
                glUniformMatrix2x4fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT4x2:
                glUniformMatrix4x2fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT3x4:
                glUniformMatrix3x4fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;
            case GL_FLOAT_MAT4x3:
                glUniformMatrix4x3fv(
                    location, size, false, reinterpret_cast<const GLfloat*>(zero));
                break;

            default:
                NOTREACHED();
                break;
            }
        }
    }

    namespace {

        struct UniformData {
            UniformData()
                : size(-1)
                , type(GL_NONE)
                , location(0)
                , added(false)
            {
            }
            std::string queried_name;
            std::string corrected_name;
            std::string original_name;
            GLsizei size;
            GLenum type;
            GLint location;
            bool added;
        };

        struct UniformDataComparer {
            bool operator()(const UniformData& lhs, const UniformData& rhs) const
            {
                return lhs.queried_name < rhs.queried_name;
            }
        };

    } // anonymous namespace

    void Program::Update()
    {
        Reset();
        UpdateLogInfo();
        link_status_ = true;
        uniforms_cleared_ = false;
        GLint num_attribs = 0;
        GLint max_len = 0;
        GLint max_location = -1;
        glGetProgramiv(service_id_, GL_ACTIVE_ATTRIBUTES, &num_attribs);
        glGetProgramiv(service_id_, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len);
        // TODO(gman): Should we check for error?
        scoped_ptr<char[]> name_buffer(new char[max_len]);
        for (GLint ii = 0; ii < num_attribs; ++ii) {
            GLsizei length = 0;
            GLsizei size = 0;
            GLenum type = 0;
            glGetActiveAttrib(
                service_id_, ii, max_len, &length, &size, &type, name_buffer.get());
            DCHECK(max_len == 0 || length < max_len);
            DCHECK(length == 0 || name_buffer[length] == '\0');
            std::string original_name;
            GetVertexAttribData(name_buffer.get(), &original_name, &type);
            // TODO(gman): Should we check for error?
            GLint location = glGetAttribLocation(service_id_, name_buffer.get());
            if (location > max_location) {
                max_location = location;
            }
            attrib_infos_.push_back(VertexAttrib(1, type, original_name, location));
            max_attrib_name_length_ = std::max(
                max_attrib_name_length_, static_cast<GLsizei>(original_name.size()));
        }

        // Create attrib location to index map.
        attrib_location_to_index_map_.resize(max_location + 1);
        for (GLint ii = 0; ii <= max_location; ++ii) {
            attrib_location_to_index_map_[ii] = -1;
        }
        for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) {
            const VertexAttrib& info = attrib_infos_[ii];
            attrib_location_to_index_map_[info.location] = ii;
        }

#if !defined(NDEBUG)
        if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                switches::kEnableGPUServiceLoggingGPU)) {
            DVLOG(1) << "----: attribs for service_id: " << service_id();
            for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) {
                const VertexAttrib& info = attrib_infos_[ii];
                DVLOG(1) << ii << ": loc = " << info.location
                         << ", size = " << info.size
                         << ", type = " << GLES2Util::GetStringEnum(info.type)
                         << ", name = " << info.name;
            }
        }
#endif

        max_len = 0;
        GLint num_uniforms = 0;
        glGetProgramiv(service_id_, GL_ACTIVE_UNIFORMS, &num_uniforms);
        glGetProgramiv(service_id_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len);
        name_buffer.reset(new char[max_len]);

        // Reads all the names.
        std::vector<UniformData> uniform_data;
        for (GLint ii = 0; ii < num_uniforms; ++ii) {
            GLsizei length = 0;
            UniformData data;
            glGetActiveUniform(
                service_id_, ii, max_len, &length,
                &data.size, &data.type, name_buffer.get());
            DCHECK(max_len == 0 || length < max_len);
            DCHECK(length == 0 || name_buffer[length] == '\0');
            data.queried_name = std::string(name_buffer.get());
            GetCorrectedUniformData(data.queried_name, &data.corrected_name,
                &data.original_name, &data.size, &data.type);
            uniform_data.push_back(data);
        }

        // NOTE: We don't care if 2 uniforms are bound to the same location.
        // One of them will take preference. The spec allows this, same as
        // BindAttribLocation.
        //
        // The reason we don't check is if we were to fail we'd have to
        // restore the previous program but since we've already linked successfully
        // at this point the previous program is gone.

        // Assigns the uniforms with bindings.
        size_t next_available_index = 0;
        for (size_t ii = 0; ii < uniform_data.size(); ++ii) {
            UniformData& data = uniform_data[ii];
            // Force builtin uniforms (gl_DepthRange) to have invalid location.
            if (ProgramManager::IsInvalidPrefix(data.queried_name.c_str(),
                    data.queried_name.size())) {
                data.location = -1;
            } else {
                data.location = glGetUniformLocation(service_id_, data.queried_name.c_str());
            }
            // remove "[0]"
            std::string short_name;
            int element_index = 0;
            bool good = GetUniformNameSansElement(data.queried_name, &element_index,
                &short_name);
            DCHECK(good);
            LocationMap::const_iterator it = bind_uniform_location_map_.find(
                short_name);
            if (it != bind_uniform_location_map_.end()) {
                AddUniformInfo(
                    data.size, data.type, data.location, it->second, data.corrected_name,
                    data.original_name, &next_available_index);
                data.added = true;
            }
        }

        // Assigns the uniforms that were not bound.
        for (size_t ii = 0; ii < uniform_data.size(); ++ii) {
            const UniformData& data = uniform_data[ii];
            if (!data.added) {
                AddUniformInfo(
                    data.size, data.type, data.location, -1, data.corrected_name,
                    data.original_name, &next_available_index);
            }
        }

#if !defined(NDEBUG)
        if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                switches::kEnableGPUServiceLoggingGPU)) {
            DVLOG(1) << "----: uniforms for service_id: " << service_id();
            for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
                const UniformInfo& info = uniform_infos_[ii];
                if (info.IsValid()) {
                    DVLOG(1) << ii << ": loc = " << info.element_locations[0]
                             << ", size = " << info.size
                             << ", type = " << GLES2Util::GetStringEnum(info.type)
                             << ", name = " << info.name;
                }
            }
        }
#endif

        valid_ = true;
    }

    void Program::ExecuteBindAttribLocationCalls()
    {
        for (const auto& key_value : bind_attrib_location_map_) {
            const std::string* mapped_name = GetAttribMappedName(key_value.first);
            if (mapped_name)
                glBindAttribLocation(service_id_, key_value.second, mapped_name->c_str());
        }
    }

    bool Program::ExecuteTransformFeedbackVaryingsCall()
    {
        if (!transform_feedback_varyings_.empty()) {
            Shader* vertex_shader = attached_shaders_[0].get();
            if (!vertex_shader) {
                set_log_info("TransformFeedbackVaryings: missing vertex shader");
                return false;
            }

            std::vector<const char*> mapped_names;
            mapped_names.reserve(transform_feedback_varyings_.size());
            for (StringVector::const_iterator it = transform_feedback_varyings_.begin();
                 it != transform_feedback_varyings_.end(); ++it) {
                const std::string& orig = *it;
                const std::string* mapped = vertex_shader->GetVaryingMappedName(orig);
                if (!mapped) {
                    std::string log = "TransformFeedbackVaryings: no varying named " + orig;
                    set_log_info(log.c_str());
                    return false;
                }
                mapped_names.push_back(mapped->c_str());
            }
            glTransformFeedbackVaryings(service_id_,
                mapped_names.size(),
                &mapped_names.front(),
                transform_feedback_buffer_mode_);
        }

        return true;
    }

    bool Program::Link(ShaderManager* manager,
        Program::VaryingsPackingOption varyings_packing_option,
        const ShaderCacheCallback& shader_callback)
    {
        ClearLinkStatus();

        if (!AttachedShadersExist()) {
            set_log_info("missing shaders");
            return false;
        }

        TimeTicks before_time = TimeTicks::Now();
        bool link = true;
        ProgramCache* cache = manager_->program_cache_;
        if (cache) {
            DCHECK(!attached_shaders_[0]->last_compiled_source().empty() && !attached_shaders_[1]->last_compiled_source().empty());
            ProgramCache::LinkedProgramStatus status = cache->GetLinkedProgramStatus(
                attached_shaders_[0]->last_compiled_signature(),
                attached_shaders_[1]->last_compiled_signature(),
                &bind_attrib_location_map_,
                transform_feedback_varyings_,
                transform_feedback_buffer_mode_);

            bool cache_hit = status == ProgramCache::LINK_SUCCEEDED;
            UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.CacheHit", cache_hit);

            if (cache_hit) {
                ProgramCache::ProgramLoadResult success = cache->LoadLinkedProgram(service_id(),
                    attached_shaders_[0].get(),
                    attached_shaders_[1].get(),
                    &bind_attrib_location_map_,
                    transform_feedback_varyings_,
                    transform_feedback_buffer_mode_,
                    shader_callback);
                link = success != ProgramCache::PROGRAM_LOAD_SUCCESS;
                UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.LoadBinarySuccess", !link);
            }
        }

        if (link) {
            CompileAttachedShaders();

            if (!CanLink()) {
                set_log_info("invalid shaders");
                return false;
            }
            if (DetectShaderVersionMismatch()) {
                set_log_info("Versions of linked shaders have to match.");
                return false;
            }
            if (DetectAttribLocationBindingConflicts()) {
                set_log_info("glBindAttribLocation() conflicts");
                return false;
            }
            std::string conflicting_name;
            if (DetectUniformsMismatch(&conflicting_name)) {
                std::string info_log = "Uniforms with the same name but different "
                                       "type/precision: "
                    + conflicting_name;
                set_log_info(ProcessLogInfo(info_log).c_str());
                return false;
            }
            if (DetectUniformLocationBindingConflicts()) {
                set_log_info("glBindUniformLocationCHROMIUM() conflicts");
                return false;
            }
            if (DetectVaryingsMismatch(&conflicting_name)) {
                std::string info_log = "Varyings with the same name but different type, "
                                       "or statically used varyings in fragment shader "
                                       "are not declared in vertex shader: "
                    + conflicting_name;
                set_log_info(ProcessLogInfo(info_log).c_str());
                return false;
            }
            if (DetectBuiltInInvariantConflicts()) {
                set_log_info("Invariant settings for certain built-in varyings "
                             "have to match");
                return false;
            }
            if (DetectGlobalNameConflicts(&conflicting_name)) {
                std::string info_log = "Name conflicts between an uniform and an "
                                       "attribute: "
                    + conflicting_name;
                set_log_info(ProcessLogInfo(info_log).c_str());
                return false;
            }
            if (!CheckVaryingsPacking(varyings_packing_option)) {
                set_log_info("Varyings over maximum register limit");
                return false;
            }

            ExecuteBindAttribLocationCalls();
            if (!ExecuteTransformFeedbackVaryingsCall()) {
                return false;
            }
            before_time = TimeTicks::Now();
            if (cache && gfx::g_driver_gl.ext.b_GL_ARB_get_program_binary) {
                glProgramParameteri(service_id(),
                    PROGRAM_BINARY_RETRIEVABLE_HINT,
                    GL_TRUE);
            }
            glLinkProgram(service_id());
        }

        GLint success = 0;
        glGetProgramiv(service_id(), GL_LINK_STATUS, &success);
        if (success == GL_TRUE) {
            Update();
            if (link) {
                if (cache) {
                    cache->SaveLinkedProgram(service_id(),
                        attached_shaders_[0].get(),
                        attached_shaders_[1].get(),
                        &bind_attrib_location_map_,
                        transform_feedback_varyings_,
                        transform_feedback_buffer_mode_,
                        shader_callback);
                }
                UMA_HISTOGRAM_CUSTOM_COUNTS(
                    "GPU.ProgramCache.BinaryCacheMissTime",
                    static_cast<base::HistogramBase::Sample>(
                        (TimeTicks::Now() - before_time).InMicroseconds()),
                    0,
                    static_cast<base::HistogramBase::Sample>(
                        TimeDelta::FromSeconds(10).InMicroseconds()),
                    50);
            } else {
                UMA_HISTOGRAM_CUSTOM_COUNTS(
                    "GPU.ProgramCache.BinaryCacheHitTime",
                    static_cast<base::HistogramBase::Sample>(
                        (TimeTicks::Now() - before_time).InMicroseconds()),
                    0,
                    static_cast<base::HistogramBase::Sample>(
                        TimeDelta::FromSeconds(1).InMicroseconds()),
                    50);
            }
        } else {
            UpdateLogInfo();
        }
        return success == GL_TRUE;
    }

    void Program::Validate()
    {
        if (!IsValid()) {
            set_log_info("program not linked");
            return;
        }
        glValidateProgram(service_id());
        UpdateLogInfo();
    }

    GLint Program::GetUniformFakeLocation(
        const std::string& name) const
    {
        bool getting_array_location = false;
        size_t open_pos = std::string::npos;
        int index = 0;
        if (!GLES2Util::ParseUniformName(
                name, &open_pos, &index, &getting_array_location)) {
            return -1;
        }
        for (GLuint ii = 0; ii < uniform_infos_.size(); ++ii) {
            const UniformInfo& info = uniform_infos_[ii];
            if (!info.IsValid()) {
                continue;
            }
            if (info.name == name || (info.is_array && info.name.compare(0, info.name.size() - 3, name) == 0)) {
                return info.fake_location_base;
            } else if (getting_array_location && info.is_array) {
                // Look for an array specification.
                size_t open_pos_2 = info.name.find_last_of('[');
                if (open_pos_2 == open_pos && name.compare(0, open_pos, info.name, 0, open_pos) == 0) {
                    if (index >= 0 && index < info.size) {
                        DCHECK_GT(static_cast<int>(info.element_locations.size()), index);
                        if (info.element_locations[index] == -1)
                            return -1;
                        return ProgramManager::MakeFakeLocation(
                            info.fake_location_base, index);
                    }
                }
            }
        }
        return -1;
    }

    GLint Program::GetAttribLocation(
        const std::string& original_name) const
    {
        for (GLuint ii = 0; ii < attrib_infos_.size(); ++ii) {
            const VertexAttrib& info = attrib_infos_[ii];
            if (info.name == original_name) {
                return info.location;
            }
        }
        return -1;
    }

    const Program::UniformInfo*
    Program::GetUniformInfoByFakeLocation(
        GLint fake_location, GLint* real_location, GLint* array_index) const
    {
        DCHECK(real_location);
        DCHECK(array_index);
        if (fake_location < 0) {
            return NULL;
        }

        GLint uniform_index = GetUniformInfoIndexFromFakeLocation(fake_location);
        if (uniform_index >= 0 && static_cast<size_t>(uniform_index) < uniform_infos_.size()) {
            const UniformInfo& uniform_info = uniform_infos_[uniform_index];
            if (!uniform_info.IsValid()) {
                return NULL;
            }
            GLint element_index = GetArrayElementIndexFromFakeLocation(fake_location);
            if (element_index < uniform_info.size) {
                *real_location = uniform_info.element_locations[element_index];
                *array_index = element_index;
                return &uniform_info;
            }
        }
        return NULL;
    }

    const std::string* Program::GetAttribMappedName(
        const std::string& original_name) const
    {
        for (auto shader : attached_shaders_) {
            if (shader) {
                const std::string* mapped_name = shader->GetAttribMappedName(original_name);
                if (mapped_name)
                    return mapped_name;
            }
        }
        return nullptr;
    }

    const std::string* Program::GetUniformMappedName(
        const std::string& original_name) const
    {
        for (auto shader : attached_shaders_) {
            if (shader) {
                const std::string* mapped_name = shader->GetUniformMappedName(original_name);
                if (mapped_name)
                    return mapped_name;
            }
        }
        return nullptr;
    }

    const std::string* Program::GetOriginalNameFromHashedName(
        const std::string& hashed_name) const
    {
        for (auto shader : attached_shaders_) {
            if (shader) {
                const std::string* original_name = shader->GetOriginalNameFromHashedName(hashed_name);
                if (original_name)
                    return original_name;
            }
        }
        return nullptr;
    }

    bool Program::SetUniformLocationBinding(
        const std::string& name, GLint location)
    {
        std::string short_name;
        int element_index = 0;
        if (!GetUniformNameSansElement(name, &element_index, &short_name) || element_index != 0) {
            return false;
        }
        bind_uniform_location_map_[short_name] = location;
        return true;
    }

    // Note: This is only valid to call right after a program has been linked
    // successfully.
    void Program::GetCorrectedUniformData(
        const std::string& name,
        std::string* corrected_name, std::string* original_name,
        GLsizei* size, GLenum* type) const
    {
        DCHECK(corrected_name && original_name && size && type);
        for (auto shader : attached_shaders_) {
            if (!shader)
                continue;
            const sh::ShaderVariable* info = NULL;
            const sh::Uniform* uniform = shader->GetUniformInfo(name);
            bool found = false;
            if (uniform)
                found = uniform->findInfoByMappedName(name, &info, original_name);
            if (found) {
                const std::string kArraySpec("[0]");
                if (info->arraySize > 0 && !base::EndsWith(name, kArraySpec, base::CompareCase::SENSITIVE)) {
                    *corrected_name = name + kArraySpec;
                    *original_name += kArraySpec;
                } else {
                    *corrected_name = name;
                }
                *type = info->type;
                *size = std::max(1u, info->arraySize);
                return;
            }
        }
        // TODO(zmo): this path should never be reached unless there is a serious
        // bug in the driver or in ANGLE translator.
        *corrected_name = name;
        *original_name = name;
    }

    void Program::GetVertexAttribData(
        const std::string& name, std::string* original_name, GLenum* type) const
    {
        DCHECK(original_name);
        DCHECK(type);
        Shader* shader = attached_shaders_[ShaderTypeToIndex(GL_VERTEX_SHADER)].get();
        if (shader) {
            // Vertex attributes can not be arrays or structs (GLSL ES 3.00.4, section
            // 4.3.4, "Input Variables"), so the top level sh::Attribute returns the
            // information we need.
            const sh::Attribute* info = shader->GetAttribInfo(name);
            if (info) {
                *original_name = info->name;
                *type = info->type;
                return;
            }
        }
        // TODO(zmo): this path should never be reached unless there is a serious
        // bug in the driver or in ANGLE translator.
        *original_name = name;
    }

    void Program::AddUniformInfo(
        GLsizei size, GLenum type, GLint location, GLint fake_base_location,
        const std::string& name, const std::string& original_name,
        size_t* next_available_index)
    {
        DCHECK(next_available_index);
        const char* kArraySpec = "[0]";
        size_t uniform_index = fake_base_location >= 0 ? fake_base_location : *next_available_index;
        if (uniform_infos_.size() < uniform_index + 1) {
            uniform_infos_.resize(uniform_index + 1);
        }

        // Before linking, we already validated that no two statically used uniforms
        // are bound to the same location.
        DCHECK(!uniform_infos_[uniform_index].IsValid());

        uniform_infos_[uniform_index] = UniformInfo(
            size, type, uniform_index, original_name);
        ++num_uniforms_;

        UniformInfo& info = uniform_infos_[uniform_index];
        info.element_locations.resize(size);
        info.element_locations[0] = location;
        DCHECK_GE(size, 0);
        size_t num_texture_units = info.IsSampler() ? static_cast<size_t>(size) : 0u;
        info.texture_units.clear();
        info.texture_units.resize(num_texture_units, 0);

        if (size > 1) {
            // Go through the array element locations looking for a match.
            // We can skip the first element because it's the same as the
            // the location without the array operators.
            size_t array_pos = name.rfind(kArraySpec);
            std::string base_name = name;
            if (name.size() > 3) {
                if (array_pos != name.size() - 3) {
                    info.name = name + kArraySpec;
                } else {
                    base_name = name.substr(0, name.size() - 3);
                }
            }
            for (GLsizei ii = 1; ii < info.size; ++ii) {
                std::string element_name(base_name + "[" + base::IntToString(ii) + "]");
                info.element_locations[ii] = glGetUniformLocation(service_id_, element_name.c_str());
            }
        }

        info.is_array = (size > 1 || (info.name.size() > 3 && info.name.rfind(kArraySpec) == info.name.size() - 3));

        if (info.IsSampler()) {
            sampler_indices_.push_back(info.fake_location_base);
        }
        max_uniform_name_length_ = std::max(max_uniform_name_length_,
            static_cast<GLsizei>(info.name.size()));

        while (*next_available_index < uniform_infos_.size() && uniform_infos_[*next_available_index].IsValid()) {
            *next_available_index = *next_available_index + 1;
        }
    }

    const Program::UniformInfo*
    Program::GetUniformInfo(
        GLint index) const
    {
        if (static_cast<size_t>(index) >= uniform_infos_.size()) {
            return NULL;
        }

        const UniformInfo& info = uniform_infos_[index];
        return info.IsValid() ? &info : NULL;
    }

    bool Program::SetSamplers(
        GLint num_texture_units, GLint fake_location,
        GLsizei count, const GLint* value)
    {
        if (fake_location < 0) {
            return true;
        }
        GLint uniform_index = GetUniformInfoIndexFromFakeLocation(fake_location);
        if (uniform_index >= 0 && static_cast<size_t>(uniform_index) < uniform_infos_.size()) {
            UniformInfo& info = uniform_infos_[uniform_index];
            if (!info.IsValid()) {
                return false;
            }
            GLint element_index = GetArrayElementIndexFromFakeLocation(fake_location);
            if (element_index < info.size) {
                count = std::min(info.size - element_index, count);
                if (info.IsSampler() && count > 0) {
                    for (GLsizei ii = 0; ii < count; ++ii) {
                        if (value[ii] < 0 || value[ii] >= num_texture_units) {
                            return false;
                        }
                    }
                    std::copy(value, value + count,
                        info.texture_units.begin() + element_index);
                    return true;
                }
            }
        }
        return true;
    }

    void Program::GetProgramiv(GLenum pname, GLint* params)
    {
        switch (pname) {
        case GL_ACTIVE_ATTRIBUTES:
            *params = attrib_infos_.size();
            break;
        case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
            // Notice +1 to accomodate NULL terminator.
            *params = max_attrib_name_length_ + 1;
            break;
        case GL_ACTIVE_UNIFORMS:
            *params = num_uniforms_;
            break;
        case GL_ACTIVE_UNIFORM_MAX_LENGTH:
            // Notice +1 to accomodate NULL terminator.
            *params = max_uniform_name_length_ + 1;
            break;
        case GL_LINK_STATUS:
            *params = link_status_;
            break;
        case GL_INFO_LOG_LENGTH:
            // Notice +1 to accomodate NULL terminator.
            *params = log_info_.get() ? (log_info_->size() + 1) : 0;
            break;
        case GL_DELETE_STATUS:
            *params = deleted_;
            break;
        case GL_VALIDATE_STATUS:
            if (!IsValid()) {
                *params = GL_FALSE;
            } else {
                glGetProgramiv(service_id_, pname, params);
            }
            break;
        default:
            glGetProgramiv(service_id_, pname, params);
            break;
        }
    }

    bool Program::AttachShader(
        ShaderManager* shader_manager,
        Shader* shader)
    {
        DCHECK(shader_manager);
        DCHECK(shader);
        int index = ShaderTypeToIndex(shader->shader_type());
        if (attached_shaders_[index].get() != NULL) {
            return false;
        }
        attached_shaders_[index] = scoped_refptr<Shader>(shader);
        shader_manager->UseShader(shader);
        return true;
    }

    bool Program::DetachShader(
        ShaderManager* shader_manager,
        Shader* shader)
    {
        DCHECK(shader_manager);
        DCHECK(shader);
        if (attached_shaders_[ShaderTypeToIndex(shader->shader_type())].get() != shader) {
            return false;
        }
        attached_shaders_[ShaderTypeToIndex(shader->shader_type())] = NULL;
        shader_manager->UnuseShader(shader);
        return true;
    }

    void Program::DetachShaders(ShaderManager* shader_manager)
    {
        DCHECK(shader_manager);
        for (auto shader : attached_shaders_) {
            if (shader) {
                DetachShader(shader_manager, shader.get());
            }
        }
    }

    void Program::CompileAttachedShaders()
    {
        for (auto shader : attached_shaders_) {
            if (shader) {
                shader->DoCompile();
            }
        }
    }

    bool Program::AttachedShadersExist() const
    {
        for (auto shader : attached_shaders_) {
            if (!shader)
                return false;
        }
        return true;
    }

    bool Program::CanLink() const
    {
        for (auto shader : attached_shaders_) {
            if (!shader || !shader->valid()) {
                return false;
            }
        }
        return true;
    }

    bool Program::DetectShaderVersionMismatch() const
    {
        int version = Shader::kUndefinedShaderVersion;
        for (auto shader : attached_shaders_) {
            if (shader) {
                if (version != Shader::kUndefinedShaderVersion && shader->shader_version() != version) {
                    return true;
                }
                version = shader->shader_version();
                DCHECK(version != Shader::kUndefinedShaderVersion);
            }
        }
        return false;
    }

    bool Program::DetectAttribLocationBindingConflicts() const
    {
        std::set<GLint> location_binding_used;
        for (const auto& key_value : bind_attrib_location_map_) {
            // Find out if an attribute is statically used in this program's shaders.
            const sh::Attribute* attrib = NULL;
            const std::string* mapped_name = GetAttribMappedName(key_value.first);
            if (!mapped_name)
                continue;
            for (auto shader : attached_shaders_) {
                if (!shader || !shader->valid())
                    continue;
                attrib = shader->GetAttribInfo(*mapped_name);
                if (attrib) {
                    if (attrib->staticUse)
                        break;
                    else
                        attrib = NULL;
                }
            }
            if (attrib) {
                size_t num_of_locations = 1;
                switch (attrib->type) {
                case GL_FLOAT_MAT2:
                    num_of_locations = 2;
                    break;
                case GL_FLOAT_MAT3:
                    num_of_locations = 3;
                    break;
                case GL_FLOAT_MAT4:
                    num_of_locations = 4;
                    break;
                default:
                    break;
                }
                for (size_t ii = 0; ii < num_of_locations; ++ii) {
                    GLint loc = key_value.second + ii;
                    auto result = location_binding_used.insert(loc);
                    if (!result.second)
                        return true;
                }
            }
        }
        return false;
    }

    bool Program::DetectUniformLocationBindingConflicts() const
    {
        std::set<GLint> location_binding_used;
        for (auto it : bind_uniform_location_map_) {
            // Find out if an attribute is statically used in this program's shaders.
            const sh::Uniform* uniform = nullptr;
            const std::string* mapped_name = GetUniformMappedName(it.first);
            if (!mapped_name)
                continue;
            for (auto shader : attached_shaders_) {
                if (!shader || !shader->valid())
                    continue;
                uniform = shader->GetUniformInfo(*mapped_name);
                if (uniform) {
                    if (uniform->staticUse)
                        break;
                    else
                        uniform = nullptr;
                }
            }
            if (uniform) {
                auto result = location_binding_used.insert(it.second);
                if (!result.second)
                    return true;
            }
        }
        return false;
    }

    bool Program::DetectUniformsMismatch(std::string* conflicting_name) const
    {
        typedef std::map<std::string, const sh::Uniform*> UniformPointerMap;
        UniformPointerMap uniform_pointer_map;
        for (auto shader : attached_shaders_) {
            const UniformMap& shader_uniforms = shader->uniform_map();
            for (const auto& key_value : shader_uniforms) {
                const std::string& name = key_value.first;
                UniformPointerMap::iterator hit = uniform_pointer_map.find(name);
                if (hit == uniform_pointer_map.end()) {
                    uniform_pointer_map[name] = &(key_value.second);
                } else {
                    // If a uniform is in the map, i.e., it has already been declared by
                    // another shader, then the type, precision, etc. must match.
                    if (hit->second->isSameUniformAtLinkTime(key_value.second))
                        continue;
                    *conflicting_name = name;
                    return true;
                }
            }
        }
        return false;
    }

    bool Program::DetectVaryingsMismatch(std::string* conflicting_name) const
    {
        DCHECK(attached_shaders_[0].get() && attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && attached_shaders_[1].get() && attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER);
        const VaryingMap* vertex_varyings = &(attached_shaders_[0]->varying_map());
        const VaryingMap* fragment_varyings = &(attached_shaders_[1]->varying_map());

        int shader_version = attached_shaders_[0]->shader_version();

        for (const auto& key_value : *fragment_varyings) {
            const std::string& name = key_value.first;
            if (IsBuiltInFragmentVarying(name))
                continue;

            VaryingMap::const_iterator hit = vertex_varyings->find(name);
            if (hit == vertex_varyings->end()) {
                if (key_value.second.staticUse) {
                    *conflicting_name = name;
                    return true;
                }
                continue;
            }

            if (!hit->second.isSameVaryingAtLinkTime(key_value.second,
                    shader_version)) {
                *conflicting_name = name;
                return true;
            }
        }
        return false;
    }

    bool Program::DetectBuiltInInvariantConflicts() const
    {
        DCHECK(attached_shaders_[0].get() && attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && attached_shaders_[1].get() && attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER);
        const VaryingMap& vertex_varyings = attached_shaders_[0]->varying_map();
        const VaryingMap& fragment_varyings = attached_shaders_[1]->varying_map();

        bool gl_position_invariant = IsBuiltInInvariant(
            vertex_varyings, "gl_Position");
        bool gl_point_size_invariant = IsBuiltInInvariant(
            vertex_varyings, "gl_PointSize");

        bool gl_frag_coord_invariant = IsBuiltInInvariant(
            fragment_varyings, "gl_FragCoord");
        bool gl_point_coord_invariant = IsBuiltInInvariant(
            fragment_varyings, "gl_PointCoord");

        return ((gl_frag_coord_invariant && !gl_position_invariant) || (gl_point_coord_invariant && !gl_point_size_invariant));
    }

    bool Program::DetectGlobalNameConflicts(std::string* conflicting_name) const
    {
        DCHECK(attached_shaders_[0].get() && attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && attached_shaders_[1].get() && attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER);
        const UniformMap* uniforms[2];
        uniforms[0] = &(attached_shaders_[0]->uniform_map());
        uniforms[1] = &(attached_shaders_[1]->uniform_map());
        const AttributeMap* attribs = &(attached_shaders_[0]->attrib_map());

        for (const auto& key_value : *attribs) {
            for (int ii = 0; ii < 2; ++ii) {
                if (uniforms[ii]->find(key_value.first) != uniforms[ii]->end()) {
                    *conflicting_name = key_value.first;
                    return true;
                }
            }
        }
        return false;
    }

    bool Program::CheckVaryingsPacking(
        Program::VaryingsPackingOption option) const
    {
        DCHECK(attached_shaders_[0].get() && attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER && attached_shaders_[1].get() && attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER);
        const VaryingMap* vertex_varyings = &(attached_shaders_[0]->varying_map());
        const VaryingMap* fragment_varyings = &(attached_shaders_[1]->varying_map());

        std::map<std::string, ShVariableInfo> combined_map;

        for (const auto& key_value : *fragment_varyings) {
            if (!key_value.second.staticUse && option == kCountOnlyStaticallyUsed)
                continue;
            if (!IsBuiltInFragmentVarying(key_value.first)) {
                VaryingMap::const_iterator vertex_iter = vertex_varyings->find(key_value.first);
                if (vertex_iter == vertex_varyings->end() || (!vertex_iter->second.staticUse && option == kCountOnlyStaticallyUsed))
                    continue;
            }

            ShVariableInfo var;
            var.type = static_cast<sh::GLenum>(key_value.second.type);
            var.size = std::max(1u, key_value.second.arraySize);
            combined_map[key_value.first] = var;
        }

        if (combined_map.size() == 0)
            return true;
        scoped_ptr<ShVariableInfo[]> variables(
            new ShVariableInfo[combined_map.size()]);
        size_t index = 0;
        for (const auto& key_value : combined_map) {
            variables[index].type = key_value.second.type;
            variables[index].size = key_value.second.size;
            ++index;
        }
        return ShCheckVariablesWithinPackingLimits(
            static_cast<int>(manager_->max_varying_vectors()),
            variables.get(),
            combined_map.size());
    }

    void Program::GetProgramInfo(
        ProgramManager* manager, CommonDecoder::Bucket* bucket) const
    {
        // NOTE: It seems to me the math in here does not need check for overflow
        // because the data being calucated from has various small limits. The max
        // number of attribs + uniforms is somewhere well under 1024. The maximum size
        // of an identifier is 256 characters.
        uint32 num_locations = 0;
        uint32 total_string_size = 0;

        for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) {
            const VertexAttrib& info = attrib_infos_[ii];
            num_locations += 1;
            total_string_size += info.name.size();
        }

        for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
            const UniformInfo& info = uniform_infos_[ii];
            if (info.IsValid()) {
                num_locations += info.element_locations.size();
                total_string_size += info.name.size();
            }
        }

        uint32 num_inputs = attrib_infos_.size() + num_uniforms_;
        uint32 input_size = num_inputs * sizeof(ProgramInput);
        uint32 location_size = num_locations * sizeof(int32);
        uint32 size = sizeof(ProgramInfoHeader) + input_size + location_size + total_string_size;

        bucket->SetSize(size);
        ProgramInfoHeader* header = bucket->GetDataAs<ProgramInfoHeader*>(0, size);
        ProgramInput* inputs = bucket->GetDataAs<ProgramInput*>(
            sizeof(ProgramInfoHeader), input_size);
        int32* locations = bucket->GetDataAs<int32*>(
            sizeof(ProgramInfoHeader) + input_size, location_size);
        char* strings = bucket->GetDataAs<char*>(
            sizeof(ProgramInfoHeader) + input_size + location_size,
            total_string_size);
        DCHECK(header);
        DCHECK(inputs);
        DCHECK(locations);
        DCHECK(strings);

        header->link_status = link_status_;
        header->num_attribs = attrib_infos_.size();
        header->num_uniforms = num_uniforms_;

        for (size_t ii = 0; ii < attrib_infos_.size(); ++ii) {
            const VertexAttrib& info = attrib_infos_[ii];
            inputs->size = info.size;
            inputs->type = info.type;
            inputs->location_offset = ComputeOffset(header, locations);
            inputs->name_offset = ComputeOffset(header, strings);
            inputs->name_length = info.name.size();
            *locations++ = info.location;
            memcpy(strings, info.name.c_str(), info.name.size());
            strings += info.name.size();
            ++inputs;
        }

        for (size_t ii = 0; ii < uniform_infos_.size(); ++ii) {
            const UniformInfo& info = uniform_infos_[ii];
            if (info.IsValid()) {
                inputs->size = info.size;
                inputs->type = info.type;
                inputs->location_offset = ComputeOffset(header, locations);
                inputs->name_offset = ComputeOffset(header, strings);
                inputs->name_length = info.name.size();
                DCHECK(static_cast<size_t>(info.size) == info.element_locations.size());
                for (size_t jj = 0; jj < info.element_locations.size(); ++jj) {
                    if (info.element_locations[jj] == -1)
                        *locations++ = -1;
                    else
                        *locations++ = ProgramManager::MakeFakeLocation(ii, jj);
                }
                memcpy(strings, info.name.c_str(), info.name.size());
                strings += info.name.size();
                ++inputs;
            }
        }

        DCHECK_EQ(ComputeOffset(header, strings), size);
    }

    bool Program::GetUniformBlocks(CommonDecoder::Bucket* bucket) const
    {
        // The data is packed into the bucket in the following order
        //   1) header
        //   2) N entries of block data (except for name and indices)
        //   3) name1, indices1, name2, indices2, ..., nameN, indicesN
        //
        // We query all the data directly through GL calls, assuming they are
        // cheap through MANGLE.

        DCHECK(bucket);
        GLuint program = service_id();

        uint32_t header_size = sizeof(UniformBlocksHeader);
        bucket->SetSize(header_size); // In case we fail.

        uint32_t num_uniform_blocks = 0;
        GLint param = GL_FALSE;
        // We assume program is a valid program service id.
        glGetProgramiv(program, GL_LINK_STATUS, &param);
        if (param == GL_TRUE) {
            param = 0;
            glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &param);
            num_uniform_blocks = static_cast<uint32_t>(param);
        }
        if (num_uniform_blocks == 0) {
            // Although spec allows an implementation to return uniform block info
            // even if a link fails, for consistency, we disallow that.
            return true;
        }

        std::vector<UniformBlockInfo> blocks(num_uniform_blocks);
        base::CheckedNumeric<uint32_t> size = sizeof(UniformBlockInfo);
        size *= num_uniform_blocks;
        uint32_t entry_size = size.ValueOrDefault(0);
        size += header_size;
        std::vector<std::string> names(num_uniform_blocks);
        GLint max_name_length = 0;
        glGetProgramiv(
            program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_name_length);
        std::vector<GLchar> buffer(max_name_length);
        GLsizei length;
        for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) {
            param = 0;
            glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_BINDING, &param);
            blocks[ii].binding = static_cast<uint32_t>(param);

            param = 0;
            glGetActiveUniformBlockiv(program, ii, GL_UNIFORM_BLOCK_DATA_SIZE, &param);
            blocks[ii].data_size = static_cast<uint32_t>(param);

            blocks[ii].name_offset = size.ValueOrDefault(0);
            param = 0;
            glGetActiveUniformBlockiv(
                program, ii, GL_UNIFORM_BLOCK_NAME_LENGTH, &param);
            DCHECK_GE(max_name_length, param);
            memset(&buffer[0], 0, param);
            length = 0;
            glGetActiveUniformBlockName(
                program, ii, static_cast<GLsizei>(param), &length, &buffer[0]);
            DCHECK_EQ(param, length + 1);
            names[ii] = std::string(&buffer[0], length);
            // TODO(zmo): optimize the name mapping lookup.
            const std::string* original_name = GetOriginalNameFromHashedName(names[ii]);
            if (original_name)
                names[ii] = *original_name;
            blocks[ii].name_length = names[ii].size() + 1;
            size += blocks[ii].name_length;

            param = 0;
            glGetActiveUniformBlockiv(
                program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &param);
            blocks[ii].active_uniforms = static_cast<uint32_t>(param);
            blocks[ii].active_uniform_offset = size.ValueOrDefault(0);
            base::CheckedNumeric<uint32_t> indices_size = blocks[ii].active_uniforms;
            indices_size *= sizeof(uint32_t);
            if (!indices_size.IsValid())
                return false;
            size += indices_size.ValueOrDefault(0);

            param = 0;
            glGetActiveUniformBlockiv(
                program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, &param);
            blocks[ii].referenced_by_vertex_shader = static_cast<uint32_t>(param);

            param = 0;
            glGetActiveUniformBlockiv(
                program, ii, GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, &param);
            blocks[ii].referenced_by_fragment_shader = static_cast<uint32_t>(param);
        }
        if (!size.IsValid())
            return false;
        uint32_t total_size = size.ValueOrDefault(0);
        DCHECK_LE(header_size + entry_size, total_size);
        uint32_t data_size = total_size - header_size - entry_size;

        bucket->SetSize(total_size);
        UniformBlocksHeader* header = bucket->GetDataAs<UniformBlocksHeader*>(0, header_size);
        UniformBlockInfo* entries = bucket->GetDataAs<UniformBlockInfo*>(
            header_size, entry_size);
        char* data = bucket->GetDataAs<char*>(header_size + entry_size, data_size);
        DCHECK(header);
        DCHECK(entries);
        DCHECK(data);

        // Copy over data for the header and entries.
        header->num_uniform_blocks = num_uniform_blocks;
        memcpy(entries, &blocks[0], entry_size);

        std::vector<GLint> params;
        for (uint32_t ii = 0; ii < num_uniform_blocks; ++ii) {
            // Get active uniform name.
            memcpy(data, names[ii].c_str(), names[ii].length() + 1);
            data += names[ii].length() + 1;

            // Get active uniform indices.
            if (params.size() < blocks[ii].active_uniforms)
                params.resize(blocks[ii].active_uniforms);
            uint32_t num_bytes = blocks[ii].active_uniforms * sizeof(GLint);
            memset(&params[0], 0, num_bytes);
            glGetActiveUniformBlockiv(
                program, ii, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &params[0]);
            uint32_t* indices = reinterpret_cast<uint32_t*>(data);
            for (uint32_t uu = 0; uu < blocks[ii].active_uniforms; ++uu) {
                indices[uu] = static_cast<uint32_t>(params[uu]);
            }
            data += num_bytes;
        }
        DCHECK_EQ(ComputeOffset(header, data), total_size);
        return true;
    }

    bool Program::GetTransformFeedbackVaryings(
        CommonDecoder::Bucket* bucket) const
    {
        // The data is packed into the bucket in the following order
        //   1) header
        //   2) N entries of varying data (except for name)
        //   3) name1, name2, ..., nameN
        //
        // We query all the data directly through GL calls, assuming they are
        // cheap through MANGLE.

        DCHECK(bucket);
        GLuint program = service_id();

        uint32_t header_size = sizeof(TransformFeedbackVaryingsHeader);
        bucket->SetSize(header_size); // In case we fail.

        GLenum transform_feedback_buffer_mode = 0;
        GLint param = 0;
        glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, &param);
        transform_feedback_buffer_mode = static_cast<GLenum>(param);

        uint32_t num_transform_feedback_varyings = 0;
        param = GL_FALSE;
        // We assume program is a valid program service id.
        glGetProgramiv(program, GL_LINK_STATUS, &param);
        if (param == GL_TRUE) {
            param = 0;
            glGetProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &param);
            num_transform_feedback_varyings = static_cast<uint32_t>(param);
        }
        if (num_transform_feedback_varyings == 0) {
            TransformFeedbackVaryingsHeader* header = bucket->GetDataAs<TransformFeedbackVaryingsHeader*>(0, header_size);
            header->transform_feedback_buffer_mode = transform_feedback_buffer_mode;
            return true;
        }

        std::vector<TransformFeedbackVaryingInfo> varyings(
            num_transform_feedback_varyings);
        base::CheckedNumeric<uint32_t> size = sizeof(TransformFeedbackVaryingInfo);
        size *= num_transform_feedback_varyings;
        uint32_t entry_size = size.ValueOrDefault(0);
        size += header_size;
        std::vector<std::string> names(num_transform_feedback_varyings);
        GLint max_name_length = 0;
        glGetProgramiv(
            program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &max_name_length);
        if (max_name_length < 1)
            max_name_length = 1;
        std::vector<char> buffer(max_name_length);
        for (uint32_t ii = 0; ii < num_transform_feedback_varyings; ++ii) {
            GLsizei var_size = 0;
            GLsizei var_name_length = 0;
            GLenum var_type = 0;
            glGetTransformFeedbackVarying(
                program, ii, max_name_length,
                &var_name_length, &var_size, &var_type, &buffer[0]);
            varyings[ii].size = static_cast<uint32_t>(var_size);
            varyings[ii].type = static_cast<uint32_t>(var_type);
            varyings[ii].name_offset = static_cast<uint32_t>(size.ValueOrDefault(0));
            DCHECK_GT(max_name_length, var_name_length);
            names[ii] = std::string(&buffer[0], var_name_length);
            // TODO(zmo): optimize the name mapping lookup.
            const std::string* original_name = GetOriginalNameFromHashedName(names[ii]);
            if (original_name)
                names[ii] = *original_name;
            varyings[ii].name_length = names[ii].size() + 1;
            size += names[ii].size();
            size += 1;
        }
        if (!size.IsValid())
            return false;
        uint32_t total_size = size.ValueOrDefault(0);
        DCHECK_LE(header_size + entry_size, total_size);
        uint32_t data_size = total_size - header_size - entry_size;

        bucket->SetSize(total_size);
        TransformFeedbackVaryingsHeader* header = bucket->GetDataAs<TransformFeedbackVaryingsHeader*>(0, header_size);
        TransformFeedbackVaryingInfo* entries = bucket->GetDataAs<TransformFeedbackVaryingInfo*>(header_size, entry_size);
        char* data = bucket->GetDataAs<char*>(header_size + entry_size, data_size);
        DCHECK(header);
        DCHECK(entries);
        DCHECK(data);

        // Copy over data for the header and entries.
        header->transform_feedback_buffer_mode = transform_feedback_buffer_mode;
        header->num_transform_feedback_varyings = num_transform_feedback_varyings;
        memcpy(entries, &varyings[0], entry_size);

        for (uint32_t ii = 0; ii < num_transform_feedback_varyings; ++ii) {
            memcpy(data, names[ii].c_str(), names[ii].length() + 1);
            data += names[ii].length() + 1;
        }
        DCHECK_EQ(ComputeOffset(header, data), total_size);
        return true;
    }

    bool Program::GetUniformsES3(CommonDecoder::Bucket* bucket) const
    {
        // The data is packed into the bucket in the following order
        //   1) header
        //   2) N entries of UniformES3Info
        //
        // We query all the data directly through GL calls, assuming they are
        // cheap through MANGLE.

        DCHECK(bucket);
        GLuint program = service_id();

        uint32_t header_size = sizeof(UniformsES3Header);
        bucket->SetSize(header_size); // In case we fail.

        GLsizei count = 0;
        GLint param = GL_FALSE;
        // We assume program is a valid program service id.
        glGetProgramiv(program, GL_LINK_STATUS, &param);
        if (param == GL_TRUE) {
            param = 0;
            glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
        }
        if (count == 0) {
            return true;
        }

        base::CheckedNumeric<uint32_t> size = sizeof(UniformES3Info);
        size *= count;
        uint32_t entry_size = size.ValueOrDefault(0);
        size += header_size;
        if (!size.IsValid())
            return false;
        uint32_t total_size = size.ValueOrDefault(0);
        bucket->SetSize(total_size);
        UniformsES3Header* header = bucket->GetDataAs<UniformsES3Header*>(0, header_size);
        DCHECK(header);
        header->num_uniforms = static_cast<uint32_t>(count);

        // Instead of GetDataAs<UniformES3Info*>, we do GetDataAs<int32_t>. This is
        // because struct UniformES3Info is defined as five int32_t.
        // By doing this, we can fill the structs through loops.
        int32_t* entries = bucket->GetDataAs<int32_t*>(header_size, entry_size);
        DCHECK(entries);
        const size_t kStride = sizeof(UniformES3Info) / sizeof(int32_t);

        const GLenum kPname[] = {
            GL_UNIFORM_BLOCK_INDEX,
            GL_UNIFORM_OFFSET,
            GL_UNIFORM_ARRAY_STRIDE,
            GL_UNIFORM_MATRIX_STRIDE,
            GL_UNIFORM_IS_ROW_MAJOR,
        };
        const GLint kDefaultValue[] = { -1, -1, -1, -1, 0 };
        const size_t kNumPnames = arraysize(kPname);
        std::vector<GLuint> indices(count);
        for (GLsizei ii = 0; ii < count; ++ii) {
            indices[ii] = ii;
        }
        std::vector<GLint> params(count);
        for (size_t pname_index = 0; pname_index < kNumPnames; ++pname_index) {
            for (GLsizei ii = 0; ii < count; ++ii) {
                params[ii] = kDefaultValue[pname_index];
            }
            glGetActiveUniformsiv(
                program, count, &indices[0], kPname[pname_index], &params[0]);
            for (GLsizei ii = 0; ii < count; ++ii) {
                entries[kStride * ii + pname_index] = params[ii];
            }
        }
        return true;
    }

    void Program::TransformFeedbackVaryings(GLsizei count,
        const char* const* varyings,
        GLenum buffer_mode)
    {
        transform_feedback_varyings_.clear();
        for (GLsizei i = 0; i < count; ++i) {
            transform_feedback_varyings_.push_back(std::string(varyings[i]));
        }
        transform_feedback_buffer_mode_ = buffer_mode;
    }

    Program::~Program()
    {
        if (manager_) {
            if (manager_->have_context_) {
                glDeleteProgram(service_id());
            }
            manager_->StopTracking(this);
            manager_ = NULL;
        }
    }

    ProgramManager::ProgramManager(ProgramCache* program_cache,
        uint32 max_varying_vectors)
        : program_count_(0)
        , have_context_(true)
        , program_cache_(program_cache)
        , max_varying_vectors_(max_varying_vectors)
    {
    }

    ProgramManager::~ProgramManager()
    {
        DCHECK(programs_.empty());
    }

    void ProgramManager::Destroy(bool have_context)
    {
        have_context_ = have_context;
        programs_.clear();
    }

    void ProgramManager::StartTracking(Program* /* program */)
    {
        ++program_count_;
    }

    void ProgramManager::StopTracking(Program* /* program */)
    {
        --program_count_;
    }

    Program* ProgramManager::CreateProgram(
        GLuint client_id, GLuint service_id)
    {
        std::pair<ProgramMap::iterator, bool> result = programs_.insert(
            std::make_pair(client_id,
                scoped_refptr<Program>(
                    new Program(this, service_id))));
        DCHECK(result.second);
        return result.first->second.get();
    }

    Program* ProgramManager::GetProgram(GLuint client_id)
    {
        ProgramMap::iterator it = programs_.find(client_id);
        return it != programs_.end() ? it->second.get() : NULL;
    }

    bool ProgramManager::GetClientId(GLuint service_id, GLuint* client_id) const
    {
        // This doesn't need to be fast. It's only used during slow queries.
        for (const auto& key_value : programs_) {
            if (key_value.second->service_id() == service_id) {
                *client_id = key_value.first;
                return true;
            }
        }
        return false;
    }

    ProgramCache* ProgramManager::program_cache() const
    {
        return program_cache_;
    }

    bool ProgramManager::IsOwned(Program* program) const
    {
        for (const auto& key_value : programs_) {
            if (key_value.second.get() == program) {
                return true;
            }
        }
        return false;
    }

    void ProgramManager::RemoveProgramInfoIfUnused(
        ShaderManager* shader_manager, Program* program)
    {
        DCHECK(shader_manager);
        DCHECK(program);
        DCHECK(IsOwned(program));
        if (program->IsDeleted() && !program->InUse()) {
            program->DetachShaders(shader_manager);
            for (ProgramMap::iterator it = programs_.begin();
                 it != programs_.end(); ++it) {
                if (it->second.get() == program) {
                    programs_.erase(it);
                    return;
                }
            }
            NOTREACHED();
        }
    }

    void ProgramManager::MarkAsDeleted(
        ShaderManager* shader_manager,
        Program* program)
    {
        DCHECK(shader_manager);
        DCHECK(program);
        DCHECK(IsOwned(program));
        program->MarkAsDeleted();
        RemoveProgramInfoIfUnused(shader_manager, program);
    }

    void ProgramManager::UseProgram(Program* program)
    {
        DCHECK(program);
        DCHECK(IsOwned(program));
        program->IncUseCount();
    }

    void ProgramManager::UnuseProgram(
        ShaderManager* shader_manager,
        Program* program)
    {
        DCHECK(shader_manager);
        DCHECK(program);
        DCHECK(IsOwned(program));
        program->DecUseCount();
        RemoveProgramInfoIfUnused(shader_manager, program);
    }

    void ProgramManager::ClearUniforms(Program* program)
    {
        DCHECK(program);
        program->ClearUniforms(&zero_);
    }

    int32 ProgramManager::MakeFakeLocation(int32 index, int32 element)
    {
        return index + element * 0x10000;
    }

} // namespace gles2
} // namespace gpu
